<?php
/**
 * 热敏纸（电子面单）相关识别
 */
namespace soa\ocr\baiduOcr;

use soa\check\waybill\Rule;//运单号规则
use artisan\http;
use artisan\db;
use soa\parter\sto\mobileAddress;
use soa\partner\zt\ztCommon;//中通公用方法

class thermalPaperOcr{
    /**
     * 热敏纸（电子面单）识别
     * @param $img //传图片url或者文件流（file_get_contents）
     * @param string $brand 快递品牌，有则传，没有则为空
     * @param string $ocrWhat 识别模式
     * @return array
     */
    public static function thermalPaperOcr($img, $brand='', $ocrWhat='all', $cmPhone){
        if(empty($img)){
            raise(1009, '没有需要识别的图像！');
        }
        $return = [];//预定义的返回结果
        $time   = time();
        $retArr = baiduOcr::generalWithLocation($img);//百度通用文字识别（含位置信息版）

        if(//返回值正常
            isset($retArr['words_result']) &&
            !empty($retArr['words_result']) &&
            is_array($retArr['words_result'])
        ){
            //旋转图片的坐标变换
            if(isset($retArr['direction']) && !in_array($retArr['direction'], ['-1', '0'])){
                $img_info = getimagesize($img);//获取图像长宽 $img_info[0]--picWidth  $img_info[1]--picHeight
                $wordsResultTemp = [];
                switch ($retArr['direction']){//图像方向，当detect_direction传‘true’时存在。
//                    case '-1'://-1:未定义，
//                        break;
//                    case '0'://0:正向，
//                        break;
                    case '1'://1:逆时针90度，
                        foreach($retArr['words_result'] as $k=>$v){
                            $wordsResultTemp[$k] = [
                                'location' => self::changeLocation($v['location'], 1, $img_info[0], $img_info[1]),
                                'words'    => $v['words'],
                            ];
                        }
                        $retArr['words_result'] = $wordsResultTemp;
                        break;
                    case '2'://2:逆时针180度，
                        foreach($retArr['words_result'] as $k=>$v){
                            $wordsResultTemp[$k] = [
                                'location' => self::changeLocation($v['location'], 2, $img_info[0], $img_info[1]),
                                'words'    => $v['words'],
                            ];
                        }
                        $retArr['words_result'] = $wordsResultTemp;
                        break;
                    case '3'://3:逆时针270度
                        foreach($retArr['words_result'] as $k=>$v){
                            $wordsResultTemp[$k] = [
                                'location' => self::changeLocation($v['location'], 3, $img_info[0], $img_info[1]),
                                'words'    => $v['words'],
                            ];
                        }
                        $retArr['words_result'] = $wordsResultTemp;
                        break;
                }
            }

            if($ocrWhat == 'nameMobileAddress'){//识别 姓名， 手机号， 地址
                $wordsArr = array_column($retArr['words_result'], 'words');
                //获取所有手机号开始
                $phoneData = self::getPhoneData($wordsArr);
                //获取所有手机号结束
//var_dump($phoneData);
                //尝试获取姓名，地址开始
                if(!empty($phoneData)){
                    foreach($phoneData as $phone=>$numArr){
                        foreach($numArr as $num){
                            $words = $wordsArr[$num];
                            preg_match_all('/^(?<name>.*)?1[\d*]{10}(.*)?$/', $words, $matchs);
                            $matchs['name'] = array_filter($matchs['name']);
                            sort($matchs['name']);
                            if(isset($matchs['name'][0]) && $matchs['name'][0]){
                                $phoneDataTemp[$phone] = [
                                    'name'   => $matchs['name'][0],
                                    'address'=> '',//$wordsArr[$num+1],
                                ];
                            }else{
                                $phoneDataTemp[$phone] = [
                                    'name'   => isset($wordsArr[$num-1]) ? $wordsArr[$num-1] : '',
                                    'address'=> '',//$wordsArr[$num+1],
                                ];
                            }
                            for($i=1;$i<=3;$i++){
                                $phoneDataTemp[$phone]['address'] = isset($wordsArr[$num+$i]) ? $phoneDataTemp[$phone]['address'].$wordsArr[$num+$i] : $phoneDataTemp[$phone]['address'];
                            }
//var_dump([
//    'phone'  => $phone,
//    'name'   => $phoneDataTemp[$phone]['name'],
//    'address'=> $phoneDataTemp[$phone]['address'],
//]);
                            return [
                                'phone'  => $phone,
                                'name'   => $phoneDataTemp[$phone]['name'],
                                'address'=> $phoneDataTemp[$phone]['address'],
                            ];
                        }
                    }
                }
                return [
                    'phone'  => '',
                    'name'   => '',
                    'address'=> '',
                ];
                //尝试获取姓名，地址结束
            }

            if($ocrWhat == 'all'){//识别 运单号；收件人电话，地址，姓名；发件人电话，地址，姓名
                $wordsArr = array_column($retArr['words_result'], 'words');
//var_dump($wordsArr);
                //获取运单号开始
                $waybillNoRet = self::getWaybill($wordsArr, $brand);
                $return['waybillNo'] = $waybillNo = $waybillNoRet['waybillNo'];//运单号
                $waybillNoWordsNum = $waybillNoRet['num'];//运单号所在文本的行号
                //获取运单号结束


                //获取所有手机号开始
                $phoneData = self::getPhoneData($wordsArr);
                //获取所有手机号结束
//var_dump($phoneData);

                if($brand){//通过运单号从快递公司接口获取运单信息
                    $receiveInfo = self::getReceiveInfo($waybillNo, $brand, $cmPhone);
//var_dump($receiveInfo);
                    if($receiveInfo['receivePhone']){
                        //找相似度高的手机号
                        $phoneSimilar = [];
                        foreach($phoneData as $phone=>$Info){
                            //判断手机号相似度
                            $phoneSimilar[$phone] = similar_text($receiveInfo['receivePhone'], $phone);
                        }
                        //与接口返回的收件人手机号相似度最大的手机号--->收件人手机号
                        $receiveKey = array_search(max($phoneSimilar), $phoneSimilar);
                        $q = similar_text($receiveInfo['receiveName'], $wordsArr[$phoneData[$receiveKey][0]-1]);
//var_dump($q);
                        $return['receiveName']   = ($q > 1 && !preg_match('/^\w*$/', $wordsArr[$phoneData[$receiveKey][0]-1])) ? $wordsArr[$phoneData[$receiveKey][0]-1] : $receiveInfo['receiveName'];
                        $return['receivePhone']  = $receiveKey;
                        $return['receiveAddress']= $receiveInfo['receiveProvince'].$receiveInfo['receiveCity'].$receiveInfo['receiveArea'].$receiveInfo['receiveAddress'];

                        //与接口返回的收件人手机号相似度最小的手机号--->发件人手机号
                        $senderKey = array_search(min($phoneSimilar), $phoneSimilar);
                        preg_match_all('/^(?<name>.*)?1[\d*]{10}(.*)?$/', $wordsArr[$phoneData[$senderKey][0]], $matchs);
                        $matchs['name'] = array_filter($matchs['name']);
                        sort($matchs['name']);
                        if(isset($matchs['name'][0]) && $matchs['name'][0]){
                            $return['senderName']   = $matchs['name'][0];
                        }else{
                            $return['senderName']   = $wordsArr[$phoneData[$senderKey][0]-1];
                        }
                        $return['senderPhone']  = $senderKey;
                        $return['senderAddress']= $wordsArr[$phoneData[$senderKey][0]+1];
                    }
                }

                //尝试获取姓名，地址开始
                if(!empty($phoneData)){
                    foreach($phoneData as $phone=>$numArr){
                        foreach($numArr as $num){
                            $words = $wordsArr[$num];
                            preg_match_all('/^(?<name>.*)?1[\d*]{10}(.*)?$/', $words, $matchs);
                            $matchs['name'] = array_filter($matchs['name']);
                            sort($matchs['name']);
                            if(isset($matchs['name'][0]) && $matchs['name'][0]){
                                $phoneDataTemp[$phone] = [
                                    'name'   => $matchs['name'][0],
                                    'address'=> $wordsArr[$num+1],
                                ];
                            }else{
                                $phoneDataTemp[$phone] = [
                                    'name'   => $wordsArr[$num-1],
                                    'address'=> $wordsArr[$num+1],
                                ];
                            }
                        }
                    }
                    //给一个出示的收发件人信息
                    if(!empty($phoneDataTemp) && is_array($phoneDataTemp)){
                        foreach($phoneDataTemp as $phone=>$info){
                            if($phone && !empty($info) && !isset($return['receivePhone'])){
                                $return['receiveName']   = $info['name'] ? $info['name'] : '';
                                $return['receivePhone']  = $phone;
                                $return['receiveAddress']= $info['address'] ? $info['address'] : '';
                                continue;
                            }
                            if($phone && !empty($info) && !isset($return['senderPhone'])){
                                $return['senderName']    = $info['name'] ? $info['name'] : '';
                                $return['senderPhone']   = $phone;
                                $return['senderAddress'] = $info['address'] ? $info['address'] : '';
                                continue;
                            }
                        }
                    }
                }
//var_dump($phoneDataTemp);
                //尝试获取姓名，地址结束
//var_dump($wordsArr);
//                foreach($retArr['words_result'] as $info){
////                    $return[]['phone'] = self::getPhone($words['words']);
//                }
            }
        }else{
            logDebug([
                'timeOut' => time() - $time,
                'originalImgPath' => $img,
                'brand' => $brand,
                'baiduOCRRet' => $retArr,
            ], 'baiduBrainOCRError_20170927');

            raise(300001, '识别失败！');
        }
//var_dump($return);

        $thermalPaperDBObj = db::connect('orders')->table('tbl_thermalpaper_ocr');
        $ret = $thermalPaperDBObj->insert([
            'ocr_time' => date('Y-m-d H:i:s'),
            'brand'    => $brand,
            'pic_path' => $img,
            'pic_path_md5'  => md5($img),
            'original_data' => json_encode($retArr, JSON_UNESCAPED_UNICODE),
            'result'        => json_encode($return, JSON_UNESCAPED_UNICODE),
        ]);
        return $return;
    }

    /**
     * 匹配出运单号
     * @param $wordsArr
     * @param string $brand
     * @return array
     */
    public static function getWaybill($wordsArr, $brand = ''){
        $return = [
            'num'       => '',//运单号出现在第几条词
            'waybillNo' => '',//运单号
        ];
        $waybillRuleObj = new Rule();
        switch ($brand){
            case 'sto'://申通
                $ruleDataArr = $waybillRuleObj->stoRule;
                $rule = '';
                foreach($ruleDataArr as $ruleData){
                    foreach($ruleData as $ruleTemp){
                        $rule = $rule.'|('.str_replace(['/^','$/'],'',$ruleTemp).')';
                    }
                }
                $rule = '/(?<waybill>'.substr($rule, 1).')/';
                foreach($wordsArr as $num=>$words){
                    $r = preg_match_all($rule, $words, $matchs);
                    $matchs['waybill'] = array_filter($matchs['waybill']);
                    sort($matchs['waybill']);
                    if(
                        $r &&
                        isset($matchs['waybill'][0]) &&
                        $matchs['waybill'][0] &&
                        preg_match('/\w{12,13}/', $matchs['waybill'][0])
                    ){
                        $return = [
                            'num'       => $num,//运单号出现在第几条词
                            'waybillNo' => $matchs['waybill'][0],//运单号
                        ];
                    }
                }
                break;

            case 'zt'://中通
                $ruleArr = $waybillRuleObj->getZtRule();//generalRule
                $rule = substr($ruleArr['generalRule'], 1);
                $rule = substr($rule, 0, -1);
                $rule = '/(?<waybill>('.str_replace('$|^', ')|(', $rule).'))/';
                foreach($wordsArr as $num=>$words){
                    $r = preg_match_all($rule, $words, $matchs);
                    $matchs['waybill'] = array_filter($matchs['waybill']);
                    sort($matchs['waybill']);
                    if(
                        $r &&
                        isset($matchs['waybill'][0]) &&
                        $matchs['waybill'][0] &&
                        preg_match('/\w{12,13}/', $matchs['waybill'][0])
                    ){
                        $return = [
                            'num'       => $num,//运单号出现在第几条词
                            'waybillNo' => $matchs['waybill'][0],//运单号
                        ];
                    }
                }
                break;
        }

        //没有匹配到运单号，采用通用的规则再尝试一次 匹配12-18位，字母数字单号
        if(empty($return['waybillNo'])){
            foreach($wordsArr as $num=>$words){
                $rule = '/(?<waybill>\w{12,18})/';
                $r = preg_match_all($rule, $words, $matchs);
                $matchs['waybill'] = array_filter($matchs['waybill']);
                sort($matchs['waybill']);
                if($r && isset($matchs['waybill'][0]) && $matchs['waybill'][0]){
                    $return = [
                        'num'       => $num,//运单号出现在第几条词
                        'waybillNo' => $matchs['waybill'][0],//运单号
                    ];
                }
            }
        }

        return $return;
    }

    /**
     * 匹配出所有手机号
     * @param $wordsArr
     * @return array
     */
    public static function getPhoneData($wordsArr){
        $return = [];
//        $rule = '/(?<phone>1[\d*]{7,10})/';
//        $rule = '/(?<phone>1[\d*]{10})/';
        $rule = '/(?<phone>1(3[0-9]|4[57]|5[0-35-9]|7[0135678]|8[0-9])[\d*]{8})/';//18251690859
        foreach($wordsArr as $num=>$words){
            $r = preg_match_all($rule, $words, $matchs);
            $matchs['phone'] = array_filter($matchs['phone']);
            sort($matchs['phone']);
//var_dump($matchs['phone']);
            if(
                $r &&
                isset($matchs['phone'][0]) &&
                $matchs['phone'][0]
            ){
                $return[$matchs['phone'][0]][] = $num;
//                $return[] = [
//                    'num'   => $num,
//                    'phone' => $matchs['phone'][0],
//                ];
            }
        }
        return $return;
    }

    /**
     * 根据运单号从快递公司总部接口获取运单收件人信息
     * @param $waybillNo
     * @param $brand
     * @return array
     */
    public static function getReceiveInfo($waybillNo, $brand, $cmPhone){
        $return = [];
        switch ($brand){
            case 'sto':
                $url = 'http://222.66.109.133:22225/stokb/recipientByBillNo.action?billCodes='.$waybillNo;
                $retJson = http::get($url);
                //[{"waybill":"3338954861887","name":"黄**","phone":"151****1813","address":"鄂城区城区湖北鄂州私立华森中学","province":"湖北省","city":"鄂州市","area":"鄂城区"}]
                $retArr = json_decode($retJson, true);
                if(
                    json_last_error() == JSON_ERROR_NONE &&
                    isset($retArr[0]) &&
                    !empty($retArr[0]) &&
                    is_array($retArr[0])
                ){
                    $return = [
                        'waybillNo' => (isset($retArr[0]['waybill']) && $retArr[0]['waybill']) ? $retArr[0]['waybill'] : '',
                        'receiveName' => (isset($retArr[0]['name']) && $retArr[0]['name']) ? $retArr[0]['name'] : '',
                        'receivePhone' => (isset($retArr[0]['phone']) && $retArr[0]['phone']) ? $retArr[0]['phone'] : '',
                        'receiveAddress' => (isset($retArr[0]['address']) && $retArr[0]['address']) ? $retArr[0]['address'] : '',
                        'receiveProvince' => (isset($retArr[0]['province']) && $retArr[0]['province']) ? $retArr[0]['province'] : '',
                        'receiveCity' => (isset($retArr[0]['city']) && $retArr[0]['city']) ? $retArr[0]['city'] : '',
                        'receiveArea' => (isset($retArr[0]['area']) && $retArr[0]['area']) ? $retArr[0]['area'] : '',
                    ];
                }
                break;

            case 'zt':
                $ztObj = new ztCommon();

                $ret = $ztObj->ztXMLInterface($cmPhone, [
                    'data'  =>'<a>'.$waybillNo.'</a>',
                    'optype'=>'G018',
                ], '1');

                $return = [
                    'waybillNo' => (isset($ret['Success']['d']['p']['a']) && $ret['Success']['d']['p']['a']) ? $ret['Success']['d']['p']['a'] : '',
//                    'receiveName' => (isset($ret['Success']['d']['p']['b']) && $ret['Success']['d']['p']['b']) ? str_replace('先生/女士', '', $ret['Success']['d']['p']['b']): '',
                    'receiveName' => (isset($ret['Success']['d']['p']['b']) && $ret['Success']['d']['p']['b']) ? $ret['Success']['d']['p']['b']: '',
                    'receivePhone' => (isset($ret['Success']['d']['p']['c']) && $ret['Success']['d']['p']['c']) ? $ret['Success']['d']['p']['c'] : '',
                    'receiveAddress' => '',
                    'receiveProvince' => '',
                    'receiveCity' => '',
                    'receiveArea' => '',
                ];
                break;
        }
        return $return;
    }

    /**
     * 图像旋转坐标变换
     * @param $locationArr //百度原始返回中的location数组
     * @param $type //图片朝向（旋转角度）
     * @param $picWidth //图片原始宽度
     * @param $picHeight //图片原始高度
     * @return array //返回与百度原始返回中的location数组格式相同
     */
    public static function changeLocation($locationArr, $type, $picWidth, $picHeight){
        $return = [];
        if($type == 1){//逆时针90°转换
            $return = [
                'width' => $locationArr['height'],
                'top'   => $locationArr['left'],
                'height'=> $locationArr['width'],
                'left'  => $picHeight - $locationArr['height'] - $locationArr['top'],
            ];
        }elseif($type == 2){//逆时针180°转换
            $return = [
                'width' => $locationArr['width'],
                'top'   => $picHeight - $locationArr['top'] - $locationArr['height'],
                'height'=> $locationArr['height'],
                'left'  => $picWidth - $locationArr['left'] - $locationArr['width'],
            ];
        }elseif($type == 3){//逆时针270°转换
            $return = [
                'width' => $locationArr['height'],
                'top'   => $picWidth - $locationArr['left'] - $locationArr['width'],
                'height'=> $locationArr['width'],
                'left'  => $locationArr['top'],
            ];
        }
        return $return;
    }
}